import type { Router, RouteRecordNormalized } from 'vue-router';
import { createRouter, createWebHashHistory } from 'vue-router';
import { cloneDeep, omit } from 'lodash-es';

import type { AppRouteModule, AppRouteRecordRaw } from '../types';
import { getParentLayout, LAYOUT, EXCEPTION_COMPONENT } from '../constant';
import { isString } from '@/utils/is';
import { getMappingMenuListResultModel } from '@/api/sys/model/menuModel';
import { mappingField } from '@/settings/routerSetting';

interface RouteTreeHelper<T = number> {
	id: T;
	parentId: T;
	[x: string]: any;
}

export type LayoutMapKey = 'LAYOUT';
const IFRAME = () => import('@/layouts/iframe/components/FrameBlank.vue');

const LayoutMap = new Map<string, () => Promise<typeof import('*.vue')>>();

LayoutMap.set('LAYOUT', LAYOUT);
LayoutMap.set('IFRAME', IFRAME);

let dynamicViewsModules: Record<string, () => Promise<Recordable>>;

function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
	dynamicViewsModules = dynamicViewsModules || import.meta.glob('../../views/**/*.{vue,tsx}');
	if (!routes) return;
	routes.forEach(item => {
		if (!item.component && item.meta?.frameSrc) {
			item.component = IFRAME;
		}
		const { component, name, children } = item;
		if (component) {
			if (isString(component)) {
				const layoutFound = LayoutMap.get((component as string).toUpperCase());
				if (layoutFound) {
					item.component = layoutFound;
				} else {
					item.component = dynamicImport(dynamicViewsModules, component as string);
				}
			}
		} else if (name) {
			item.component = getParentLayout();
		}
		children && asyncImportRoute(children);
	});
}

function dynamicImport(dynamicViewsModules: Record<string, () => Promise<Recordable>>, component: string) {
	const keys = Object.keys(dynamicViewsModules);
	const matchKeys = keys.filter(key => {
		const k = key.replace('../../views', '');
		const startFlag = component.startsWith('/');
		const endFlag = component.endsWith('.vue') || component.endsWith('.tsx');
		const startIndex = startFlag ? 0 : 1;
		const lastIndex = endFlag ? k.length : k.lastIndexOf('.');
		return k.substring(startIndex, lastIndex) === component;
	});
	if (matchKeys?.length === 1) {
		const matchKey = matchKeys[0];
		return dynamicViewsModules[matchKey];
	} else if (matchKeys?.length > 1) {
		console.warn(
			'Please do not create `.vue` and `.TSX` files with the same file name in the same hierarchical directory under the views folder. This will cause dynamic introduction failure',
		);
		return;
	} else {
		console.warn('在src/views/下找不到`' + component + '.vue` 或 `' + component + '.tsx`, 请自行创建!');
		return EXCEPTION_COMPONENT;
	}
}

function dynamicRedirect(route: AppRouteRecordRaw, redirect = ''): string {
	let path = '';
	if (route.path) {
		path = route.path.startsWith('/') ? route.path : '/' + route.path;
	}
	redirect += path;
	if (route.children?.length) {
		return dynamicRedirect(route.children[0], redirect);
	}
	return redirect;
}

// Turn background objects into routing objects
export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[], joinRedirect = false): T[] {
	routeList.forEach(route => {
		const component = route.component;
		if (component) {
			if (isString(component) && component.toUpperCase() === 'LAYOUT') {
				route.component = LayoutMap.get(component.toUpperCase());
				route.path = route.path.startsWith('/') ? route.path : '/' + route.path;
			} else if (component == LAYOUT) {
				route.path = route.path.startsWith('/') ? route.path : '/' + route.path;
			} else {
				route.children = [cloneDeep(route)];
				route.component = LAYOUT;
				route.name = `${route.name}Parent`;
				route.path = '';
				const meta = route.meta || {};
				meta.single = true;
				meta.affix = false;
				route.meta = meta;
			}
			joinRedirect && (route.redirect = dynamicRedirect(route));
		} else {
			console.warn('请正确配置路由：' + route?.name + '的component属性');
		}
		route.children && asyncImportRoute(route.children);
	});
	return routeList as unknown as T[];
}

/**
 * Convert multi-level routing to level 2 routing
 */
export function flatMultiLevelRoutes(routeModules: AppRouteModule[]) {
	const modules: AppRouteModule[] = cloneDeep(routeModules);
	for (let index = 0; index < modules.length; index++) {
		const routeModule = modules[index];
		if (!isMultipleRoute(routeModule)) {
			continue;
		}
		promoteRouteLevel(routeModule);
	}
	return modules;
}

// Routing level upgrade
function promoteRouteLevel(routeModule: AppRouteModule) {
	// Use vue-router to splice menus
	let router: Router | null = createRouter({
		routes: [routeModule as unknown as RouteRecordNormalized],
		history: createWebHashHistory(),
	});

	const routes = router.getRoutes();
	addToChildren(routes, routeModule.children || [], routeModule);
	router = null;

	routeModule.children = routeModule.children?.map(item => omit(item, 'children'));
}

// Add all sub-routes to the secondary route
function addToChildren(routes: RouteRecordNormalized[], children: AppRouteRecordRaw[], routeModule: AppRouteModule) {
	for (let index = 0; index < children.length; index++) {
		const child = children[index];
		const route = routes.find(item => item.name === child.name);
		if (!route) {
			continue;
		}
		routeModule.children = routeModule.children || [];
		if (!routeModule.children.find(item => item.name === route.name)) {
			routeModule.children?.push(route as unknown as AppRouteModule);
		}
		if (child.children?.length) {
			addToChildren(routes, child.children, routeModule);
		}
	}
}

// Determine whether the level exceeds 2 levels
function isMultipleRoute(routeModule: AppRouteModule) {
	if (!routeModule || !Reflect.has(routeModule, 'children') || !routeModule.children?.length) {
		return false;
	}

	const children = routeModule.children;

	let flag = false;
	for (let index = 0; index < children.length; index++) {
		const child = children[index];
		if (child.children?.length) {
			flag = true;
			break;
		}
	}
	return flag;
}

// ROUTE_MAPPING条件下，过滤后台返回的菜单list
export function filterRouteByBacklist(routeList: getMappingMenuListResultModel, asyncRoute: AppRouteRecordRaw[]) {
	let asyncRoutes = cloneDeep(asyncRoute);
	let routes: AppRouteRecordRaw[] = [];
	if (!routeList.length) {
		return routes;
	}
	asyncRoutes.map(item => {
		if (item.meta.ignoreAuth) {
			routes.push(item);
			return;
		}
		let route = routeList.find(route => route[mappingField.name] === item.name);
		if (route) {
			if (item.children?.length) {
				const list = findAllChildren(routeList, route[mappingField.id]) as getMappingMenuListResultModel;
				if (!list.length) {
					return;
				}
				item.children = filterRouteByBacklist(list, item.children);
			}
			routes.push(item);
		}
	});
	return routes;
}

function findAllChildren(list: RouteTreeHelper[], parentId = 0) {
	let lists: RouteTreeHelper[] = [];
	function loop(data: RouteTreeHelper[], id: number) {
		let rs = data.filter(item => item[mappingField.parentId] === id);
		if (rs.length) {
			rs.map(r => {
				loop(list, r[mappingField.id]);
			});
		}
		lists = [...rs, ...lists];
	}
	loop(list, parentId);
	return lists;
}
